package com.xiaoyilin.config;

import com.github.benmanes.caffeine.cache.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

/**
 * 缓存
 *
 * @author elinshaw
 */
@Slf4j
@EnableCaching
@Configuration
public class CaffeineConfig {

    /**
     * 最大容量(条)
     * 默认最多缓存50000条数据
     */
    private static final int DEFAULT_MAXSIZE = 50000;
    /**
     * 过期时间（秒）
     * 默认30分钟超时
     */
    private static final int DEFAULT_TTL = 30 * 60;

    /**
     * 初始化容量
     * 默认20
     */
    private static final int INITIAL_CAPACITY = 20;

    public enum Caches {
        defaultCache;

        Caches() {
        }

        Caches(int ttl) {
            this.ttl = ttl;
        }

        Caches(int ttl, int maxSize) {
            this.ttl = ttl;
            this.maxSize = maxSize;
        }

        Caches(int ttl, int maxSize, int initialCapacity) {
            this.ttl = ttl;
            this.maxSize = maxSize;
            this.initialCapacity = initialCapacity;
        }

        private int maxSize = DEFAULT_MAXSIZE;
        private int ttl = DEFAULT_TTL;
        private int initialCapacity = INITIAL_CAPACITY;

        public int getInitialCapacity() {
            return initialCapacity;
        }

        public int getMaxSize() {
            return maxSize;
        }

        public int getTtl() {
            return ttl;
        }

    }

    @Bean
    @Primary
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        ArrayList<CaffeineCache> caches = new ArrayList<>();
        for (Caches cache : Caches.values()) {
            caches.add(new CaffeineCache(cache.name(),
                    Caffeine.newBuilder()
                            .initialCapacity(cache.getInitialCapacity())
                            .expireAfterWrite(cache.getTtl(), TimeUnit.SECONDS)
                            .maximumSize(cache.getMaxSize())
                            .expireAfterAccess(500, TimeUnit.SECONDS)
                            .refreshAfterWrite(10, TimeUnit.SECONDS)
                            .writer(new CacheWriter<Object, Object>() {
                                @Override
                                public void write(Object key, Object value) {
                                    //此方法是同步阻塞的
                                    log.info("缓存写入:{}={}", key, value);
                                }

                                @Override
                                public void delete(Object key, Object value, RemovalCause cause) {
                                    log.info("缓存删除key:{}", key);
                                }
                            }).build(cacheLoader()))
            );
        }
        cacheManager.setCaches(caches);
        return cacheManager;
    }

    @Bean
    public CacheLoader<Object, Object> cacheLoader() {
        return new CacheLoader<Object, Object>() {
            @Override
            public Object load(Object key) throws Exception {
                return null;
            }

            // 重写这个方法将oldValue值返回回去，进而刷新缓存
            @Override
            public Object reload(Object key, Object oldValue) throws Exception {
                log.info("refresh key:{}", key);
                return oldValue;
            }
        };
    }


    /**
     * 单独使用
     * @return
     */
    @Bean
    public LoadingCache expiryCache() {
        LoadingCache<String, Object> loadingCache = Caffeine.newBuilder()
                .initialCapacity(100)
                .maximumSize(1000)
                .writer(new CacheWriter<Object, Object>() {
                    @Override
                    public void write(Object key, Object value) {
                        log.info("缓存写入:{}={}", key, value);
                    }

                    @Override
                    public void delete(Object key, Object value, RemovalCause cause) {
                        log.info("缓存删除key:{}", key);
                    }
                })
                .expireAfterAccess(1, TimeUnit.MINUTES)
                .build((String key) -> "刷新的数据"); //cacheload实现类,刷新时候调用
        return loadingCache;
    }

}
